//! Configuration management for R-XShell

use crate::error::{Result, ShellError};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::time::Duration;

/// Main configuration structure for R-XShell
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    /// Default shell to use
    pub default_shell: String,
    /// Default working directory
    pub default_working_dir: Option<PathBuf>,
    /// Default timeout for commands
    pub default_timeout: Option<Duration>,
    /// Environment variables to set by default
    pub default_env: HashMap<String, String>,
    /// Logging configuration
    pub logging: LoggingConfig,
    /// Execution configuration
    pub execution: ExecutionConfig,
    /// Security configuration
    pub security: SecurityConfig,
}

/// Logging configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    /// Whether to enable logging
    pub enabled: bool,
    /// Log level (trace, debug, info, warn, error)
    pub level: String,
    /// Log file path (None for stdout)
    pub file: Option<PathBuf>,
    /// Whether to log command execution
    pub log_commands: bool,
    /// Whether to log command output
    pub log_output: bool,
}

/// Execution configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionConfig {
    /// Maximum number of concurrent executions
    pub max_concurrent: usize,
    /// Default timeout in seconds
    pub default_timeout_secs: u64,
    /// Whether to inherit parent environment by default
    pub inherit_env: bool,
    /// Whether to capture stdout by default
    pub capture_stdout: bool,
    /// Whether to capture stderr by default
    pub capture_stderr: bool,
}

/// Security configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
    /// List of allowed commands (empty means all allowed)
    pub allowed_commands: Vec<String>,
    /// List of forbidden commands
    pub forbidden_commands: Vec<String>,
    /// Whether to allow shell injection
    pub allow_shell_injection: bool,
    /// Maximum command length
    pub max_command_length: usize,
}

impl Config {
    /// Create a new configuration with default values
    pub fn new() -> Self {
        Self {
            default_shell: Self::detect_default_shell(),
            default_working_dir: None,
            default_timeout: Some(Duration::from_secs(30)),
            default_env: HashMap::new(),
            logging: LoggingConfig::default(),
            execution: ExecutionConfig::default(),
            security: SecurityConfig::default(),
        }
    }
    
    /// Load configuration from a file
    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
        let content = std::fs::read_to_string(path)
            .map_err(|e| ShellError::Config(format!("Failed to read config file: {}", e)))?;
        
        let config: Config = serde_json::from_str(&content)
            .map_err(|e| ShellError::Config(format!("Failed to parse config: {}", e)))?;
        
        config.validate()?;
        Ok(config)
    }
    
    /// Save configuration to a file
    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let content = serde_json::to_string_pretty(self)
            .map_err(|e| ShellError::Config(format!("Failed to serialize config: {}", e)))?;
        
        std::fs::write(path, content)
            .map_err(|e| ShellError::Config(format!("Failed to write config file: {}", e)))?;
        
        Ok(())
    }
    
    /// Load configuration from environment variables
    pub fn load_from_env() -> Self {
        let mut config = Self::new();
        
        if let Ok(shell) = std::env::var("RXSHELL_DEFAULT_SHELL") {
            config.default_shell = shell;
        }
        
        if let Ok(timeout) = std::env::var("RXSHELL_DEFAULT_TIMEOUT") {
            if let Ok(secs) = timeout.parse::<u64>() {
                config.default_timeout = Some(Duration::from_secs(secs));
            }
        }
        
        if let Ok(log_level) = std::env::var("RXSHELL_LOG_LEVEL") {
            config.logging.level = log_level;
        }
        
        config
    }
    
    /// Merge this configuration with another, with the other taking precedence
    pub fn merge(mut self, other: Config) -> Self {
        if !other.default_shell.is_empty() {
            self.default_shell = other.default_shell;
        }
        
        if other.default_working_dir.is_some() {
            self.default_working_dir = other.default_working_dir;
        }
        
        if other.default_timeout.is_some() {
            self.default_timeout = other.default_timeout;
        }
        
        self.default_env.extend(other.default_env);
        self.logging = other.logging;
        self.execution = other.execution;
        self.security = other.security;
        
        self
    }
    
    /// Validate the configuration
    pub fn validate(&self) -> Result<()> {
        if self.default_shell.is_empty() {
            return Err(ShellError::config("Default shell cannot be empty"));
        }
        
        if let Some(ref dir) = self.default_working_dir {
            if !dir.exists() {
                return Err(ShellError::config(
                    format!("Default working directory does not exist: {}", dir.display())
                ));
            }
        }
        
        if self.execution.max_concurrent == 0 {
            return Err(ShellError::config("Max concurrent executions must be greater than 0"));
        }
        
        if self.security.max_command_length == 0 {
            return Err(ShellError::config("Max command length must be greater than 0"));
        }
        
        Ok(())
    }
    
    /// Detect the default shell for the current platform
    fn detect_default_shell() -> String {
        if cfg!(windows) {
            std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string())
        } else {
            std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())
        }
    }
    
    /// Get the configuration directory path
    pub fn config_dir() -> Result<PathBuf> {
        let home = std::env::var("HOME")
            .or_else(|_| std::env::var("USERPROFILE"))
            .map_err(|_| ShellError::config("Cannot determine home directory"))?;
        
        Ok(PathBuf::from(home).join(".rxshell"))
    }
    
    /// Get the default configuration file path
    pub fn default_config_file() -> Result<PathBuf> {
        Ok(Self::config_dir()?.join("config.json"))
    }
}

impl Default for Config {
    fn default() -> Self {
        Self::new()
    }
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            level: "info".to_string(),
            file: None,
            log_commands: true,
            log_output: false,
        }
    }
}

impl Default for ExecutionConfig {
    fn default() -> Self {
        Self {
            max_concurrent: 10,
            default_timeout_secs: 30,
            inherit_env: true,
            capture_stdout: true,
            capture_stderr: true,
        }
    }
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            allowed_commands: Vec::new(),
            forbidden_commands: vec![
                "rm -rf /".to_string(),
                "format c:".to_string(),
                "del /f /s /q c:\\*".to_string(),
            ],
            allow_shell_injection: false,
            max_command_length: 1024,
        }
    }
}

/// Configuration builder for fluent configuration creation
pub struct ConfigBuilder {
    config: Config,
}

impl ConfigBuilder {
    /// Create a new configuration builder
    pub fn new() -> Self {
        Self {
            config: Config::new(),
        }
    }
    
    /// Set the default shell
    pub fn default_shell<S: Into<String>>(mut self, shell: S) -> Self {
        self.config.default_shell = shell.into();
        self
    }
    
    /// Set the default working directory
    pub fn default_working_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
        self.config.default_working_dir = Some(dir.into());
        self
    }
    
    /// Set the default timeout
    pub fn default_timeout(mut self, timeout: Duration) -> Self {
        self.config.default_timeout = Some(timeout);
        self
    }
    
    /// Add a default environment variable
    pub fn env<K, V>(mut self, key: K, value: V) -> Self
    where
        K: Into<String>,
        V: Into<String>,
    {
        self.config.default_env.insert(key.into(), value.into());
        self
    }
    
    /// Set logging configuration
    pub fn logging(mut self, logging: LoggingConfig) -> Self {
        self.config.logging = logging;
        self
    }
    
    /// Set execution configuration
    pub fn execution(mut self, execution: ExecutionConfig) -> Self {
        self.config.execution = execution;
        self
    }
    
    /// Set security configuration
    pub fn security(mut self, security: SecurityConfig) -> Self {
        self.config.security = security;
        self
    }
    
    /// Build the configuration
    pub fn build(self) -> Result<Config> {
        self.config.validate()?;
        Ok(self.config)
    }
}

impl Default for ConfigBuilder {
    fn default() -> Self {
        Self::new()
    }
}